/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package it.wzm.memento.log;

import java.io.File;
import java.util.LinkedList;
import java.util.Iterator;
import java.util.Date;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;

/**
 *
 * @author leonardo
 */
public class Log {

    // declaring the status of log
    enum Status {
        DEAD,
        STARTUP,
        RUNNING,
        SHUTDOWN,
    }
    
    enum Severity {
        DEBUG,
        INFO,
        WARN,
        ERROR,
        FATAL,
        
    }        
    
    
    // global static variable
    private static Log l = null;
    private static LinkedList<LogBean> ll = null;
    private static File logfile = null;
    private static Status s = Status.DEAD;
    private static Severity severity = Severity.INFO; // default log level severity
    private static String logpath="/home/leonardo/memento.log";
    private static long DUMP_INTERVAL=5000; // default log level dump

    /**
     * default constructor, we are implementing singleton
     */
    private Log() {
    }

   
    // we want a log with a specific severity
    public static Log getLog(Severity severity) {
        setSeverity(severity);
        return getLog();
    }

    
    /**
     * Changing log level severity
     * @param severity
     */
    private static void setSeverity(Severity severity) {
        l.severity = severity;
    }    

    private static void setDumpInterval(long dump_interval) {
        DUMP_INTERVAL=dump_interval;
    }
    
    /**
     * Get instance of log
     * @return
     */
    public static Log getLog() {

        // simple if...we already instantiated
        // so l is returned, otherwise it is constructed
        if (l != null) {
            return l;
        } else {

            // declaring our current mode
            s = Status.STARTUP;

            // init variable
            ll = new LinkedList();
            logfile = new File(logpath);
            l = new Log();

            // adding a startup message
            LogBean sb = new LogBean();
            
            sb.setCaller("it.wzm.log.Log");
            sb.setSeverity(Severity.INFO); 
            sb.setTimestamp(new Date());
            sb.setMessage("Initializing log Web & Zen multimedia 2008");
            ll.add(sb); // adding to list
            
            
            // defining the thread as inner class
            // wich will be a private thread
            Runnable r = new Runnable() {

                BufferedWriter bw; // phisical log file
                
                public void dump() {
                    try {

                        // we are just checking if the
                        // queue is null

                        // get Iterator and browse
                        Iterator<LogBean> i = ll.iterator();
                        while (i.hasNext()) {

                            // poll LogBean from the list
                            LogBean lb = ll.poll();

                            // deciding the log severity
                            if (lb.getSeverity().compareTo(l.severity) > -1) {

                                //System.out.println("loggo...");
                                bw.append(
                                        new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(lb.getTimestamp()) 
                                        + " " + lb.getSeverity() 
                                        + " [" + lb.getCaller() + "]: " + lb.getMessage() + "\r\n"
                                );
                            }

                            lb = null; // free resource
                            
                        }

                    } catch (IOException e) {
                        System.out.println("Dump error:" + e.getMessage());
                    }
                }
                
                // implementing the run method 
                public void run() {
                    try {
                        
                        bw = new BufferedWriter(new FileWriter(logfile, true));
                        
                        // just an infinite loop
                        dumper:
                        for (;;) {

                            dump();
                                                                        
                            // checking if we want to close our logger
                            if (s.equals(Status.SHUTDOWN)) {
                                
                                LogBean lbs = new LogBean();
                                lbs.setCaller("it.wzm.memento.system");
                                lbs.setTimestamp(new Date());
                                lbs.setMessage("A shutdown request has been made, closing all resources");
                                lbs.setSeverity(Severity.INFO);
                                
                                ll.add(lbs);
                                
                                // writing down all log in queue
                                while (!ll.isEmpty()) {
                                    dump();
                                }
                                
                                // if we get here means we closed the logger
                                // and we can exit logger...
                                break dumper;
                            }
                            
                            // pausing the loop
                            Thread.sleep(DUMP_INTERVAL);
                            
                            // doing the rest
                            bw.flush();
                        }
                        
                        bw.close();

                    } catch (IOException e) {
                        System.out.println("Errore file thread " + e.getMessage());                                            
                    } catch (InterruptedException e) {
                        System.out.println("Errore thread " + e.getMessage());
                    }
                }
            };

            // declaring and starting the Thread
            Thread t = new Thread(r);
            t.setName("log");
            t.start();

            // now the log is running
            // we return it
            s = Status.RUNNING;
            return l;
        }
    }

    public static Status getStatus() {
        return s;
    }

    public void debug(Object tolog) {       
        this.add(tolog, new Throwable(), Severity.DEBUG);
    }

    public void info(Object tolog) {
        this.add(tolog, new Throwable(), Severity.INFO);
    }    
    
    public void warn(Object tolog) {
        this.add(tolog, new Throwable(), Severity.WARN);
    }    
    
    public void error(Object tolog) {
        this.add(tolog, new Throwable(), Severity.ERROR);
    }        
    
    public void fatal(Object tolog) {
        this.add(tolog, new Throwable(), Severity.FATAL);
    }
    
    
    /**
     * 
     * @param tolog
     * @param severity
     */
    private void add(Object tolog, Throwable t, Severity severity) {

        //When we are shutting down no more log entry can be accepted
        if (s==Status.SHUTDOWN) {
            return;
        }
        
        // log holder
        LogBean lb = new LogBean();
        
        // find the latest caller
        lb.setCaller(t.getStackTrace()[1].getClassName());        
        lb.setTimestamp(new Date()); // timestamp

        // decide if we want to log a string or an exception
        if (tolog instanceof Exception) {
            lb.setException((Exception) tolog);
        } else {
            lb.setMessage(tolog.toString());
        }

        lb.setSeverity(severity); // set severity

        ll.add(lb); // finally add bean to list

    }

    public static void close() {
        s = Status.SHUTDOWN;
    }
}
